// C source file with the function definitions to handle BMD's JNT1 section

// check_j3dgraph_bmd_jnt1() function
// function to check if the JNT1 section of a BMD is valid
j3dgraph_bmd_jnt1_err_cd check_j3dgraph_bmd_jnt1(j3dgraph * file)
{
  // check params
  if (file == NULL)
    goto err;
  
  // check the JNT1 character array at section start
  if (comp_bytes(((j3dgraph_gen_sec *) (file -> SEC)[J3DGRAPH_BMD_JNT1_POS]) -> START,
                 J3DGRAPH_BMD_SEC_START[J3DGRAPH_BMD_JNT1_POS][file -> ENDIAN - 1], 4) == 0)
    goto err;
  
  // TODO: add more checks
  
  // all good
  return J3DGRAPH_BMD_JNT1_ERR_NO_ERR;
  
  // invalid section
  err:
  return J3DGRAPH_BMD_JNT1_ERR_HEADER;
}

// append_j3dgraph_bmd_jnt1() function
// append a j3dgraph_bmd_jnt1 struct to an incomplete j3dgraph struct
j3dgraph_bmd_jnt1 * append_j3dgraph_bmd_jnt1(j3dgraph * file)
{
  // check if the section is valid
  j3dgraph_bmd_jnt1 * jnt1 = NULL;
  j3dgraph_bmd_jnt1_err_cd err_cd = check_j3dgraph_bmd_jnt1(file);
  if (err_cd < J3DGRAPH_BMD_JNT1_ERR_NO_ERR)
    goto err;
  
  // otherwise append the structure to j3dgraph
  jnt1 = allocate_memory(1, sizeof(j3dgraph_bmd_jnt1));
  if (jnt1 == NULL)
    goto err;
  
  // fill the structure  
  // temp variables
  umax tmp = 0;
  j3dgraph_gen_sec * sec = (file -> SEC)[J3DGRAPH_BMD_JNT1_POS];
  byte * ptr = (sec -> START) + 8;
  
  // my additions
  jnt1 -> PATH = file -> PATH;
  jnt1 -> ENDIAN = file -> ENDIAN;
  jnt1 -> ERR_CD = err_cd;
  // actual structure
  jnt1 -> START = sec -> START;
  jnt1 -> SIZE = sec -> SIZE;  
  jnt1 -> JNT_CNT = ptr; ptr += 2;
  jnt1 -> UNK_1 = ptr; ptr += 2;
  jnt1 -> JNT_TRANSF_TB_OFFSET = ptr; ptr += 4;
  jnt1 -> JNT_POS_TB_OFFSET = ptr; ptr += 4;
  jnt1 -> JNT_STR_TB_OFFSET = ptr; ptr += 4;
  
  // get joint count
  umax jnt_count = get_std_uint(16, jnt1 -> JNT_CNT, file -> ENDIAN);
  
  // joint transformation table
  jnt1 -> JNT_TRANSF_TB = ptr; ptr += jnt_count * 64;
  // joint position table
  jnt1 -> JNT_POS_TB = ptr; ptr += jnt_count * 2;
  // padding (4)
  jnt1 -> PAD_1 = ptr;
  ptr += get_j3dgraph_padding_size((jnt1 -> PAD_1) - (jnt1 -> START), false);
  
  // create the joint string table
  ptr = (jnt1 -> START) + get_std_uint(32, jnt1 -> JNT_STR_TB_OFFSET, file -> ENDIAN);
  tmp = get_j3dgraph_str_tb_size(ptr, (file -> DATA) + (file -> FSIZE) - ptr, file -> ENDIAN);
  if (tmp == 0)
    goto err;
  jnt1 -> JNT_STR_TB = get_j3dgraph_str_tb(ptr, (file -> DATA) + (file -> FSIZE) - ptr, file -> ENDIAN);
  if ((jnt1 -> JNT_STR_TB) == NULL)
    goto err;
  ptr += tmp;
  
  // final padding (32)
  jnt1 -> PAD_2 = ptr;
  
  // remove the j3dgraph_gen_sec and append the new one
  free_memory((file -> SEC)[J3DGRAPH_BMD_JNT1_POS]);
  return ((file -> SEC)[J3DGRAPH_BMD_JNT1_POS] = jnt1);
  // invalid section
  err:
  free_memory(jnt1);
  return NULL;
}

// remove_j3dgraph_bmd_jnt1() function
// function to remove a j3dgraph_bmd_jnt1 structure from a j3dgraph structure
void remove_j3dgraph_bmd_jnt1(j3dgraph * file)
{
  // check params
  if (file == NULL || (file -> SEC) == NULL)
    return;
  // free the memory allocated by append_j3dgraph_bmd_jnt1()
  close_j3dgraph_str_tb(((j3dgraph_bmd_jnt1 *) (file -> SEC)[J3DGRAPH_BMD_JNT1_POS]) -> JNT_STR_TB);
  free_memory((file -> SEC)[J3DGRAPH_BMD_JNT1_POS]);
  return;
}

// print_j3dgraph_bmd_jnt1() function
// print the information of the JNT1 section of a BMD
void print_j3dgraph_bmd_jnt1(j3dgraph_bmd_jnt1 * jnt1)
{
  // check ptr
  if (jnt1 == NULL || (jnt1 -> ERR_CD) < J3DGRAPH_BMD_JNT1_ERR_NO_ERR)
    return;
  
  // starting visual queue
  printf("\n### J3DGraph JNT1 section info start ###\n\n");
  
  // file info
  printf("File: %s\n", jnt1 -> PATH);
  printf("Section endian: %s\n", (jnt1 -> ENDIAN) == BIG ? "BIG" : "LITTLE");
  printf("Section error code: %u\n", jnt1 -> ERR_CD);
  
  // section header
  printf("\nSection start: ");
  print_ascii_arr(jnt1 -> START, 4);
  printf("Section size (bytes): %llu\n", get_std_uint(32, jnt1 -> SIZE, jnt1 -> ENDIAN));
  
  // get texture count
  umax jnt_count = get_std_uint(16, jnt1 -> JNT_CNT, jnt1 -> ENDIAN);
  printf("Joint count: %llu\n", jnt_count);
  printf("Unknown 1 (hex): %04llX\n", get_std_uint(16, jnt1 -> UNK_1, jnt1 -> ENDIAN));
  printf("Joint transformation table offset (hex): %08llX\n",
         get_std_uint(32, jnt1 -> JNT_TRANSF_TB_OFFSET, jnt1 -> ENDIAN));
  printf("Joint position table offset (hex): %08llX\n",
         get_std_uint(32, jnt1 -> JNT_POS_TB_OFFSET, jnt1 -> ENDIAN));
  printf("Joint name table offset (hex): %08llX\n", 
         get_std_uint(32, jnt1 -> JNT_STR_TB_OFFSET, jnt1 -> ENDIAN));
  
  // joint transformation table
  printf("\n");
  byte * ptr = jnt1 -> JNT_TRANSF_TB; // temp pointer
  for (umax i = 0; i < jnt_count; i++)
  {
    printf("Joint at index %llu\n", i);
    printf("Transformation type (hex): %04llX\n", get_std_uint(16, ptr, jnt1 -> ENDIAN));
    printf("Inherit parent scale: %s\n", get_std_uint(8, ptr + 2, jnt1 -> ENDIAN) == 1 ? "true" : "false");
    
    // transformation data unknown 1
    printf("Transf data unknown 1 (hex): %02llX\n", get_std_uint(8, ptr + 3, jnt1 -> ENDIAN));
    
    // scaling
    printf("Scaling XYZ (resp. to parent): ");
    printf(" %.3lf", get_float(32, ptr + 4, jnt1 -> ENDIAN));
    printf(" %.3lf", get_float(32, ptr + 8, jnt1 -> ENDIAN));
    printf(" %.3lf\n", get_float(32, ptr + 12, jnt1 -> ENDIAN));
    
    // rotation (-180/+180 degree range angles)
    printf("Rotation XYZ (resp. to parent):");
    printf(" %.3lf", (get_std_sint(16, ptr + 16, jnt1 -> ENDIAN) / 32767.0f) * 180); 
    printf(" %.3lf", (get_std_sint(16, ptr + 18, jnt1 -> ENDIAN) / 32767.0f) * 180);
    printf(" %.3lf\n", (get_std_sint(16, ptr + 20, jnt1 -> ENDIAN) / 32767.0f) * 180);
    
    // transformation data unknown 2
    printf("Transf data unknown 2 (hex): %04llX\n", get_std_uint(16, ptr + 22, jnt1 -> ENDIAN));
    
    // translation
    printf("Translation XYZ (resp. to parent):");
    printf(" %.3lf", get_float(32, ptr + 24, jnt1 -> ENDIAN));
    printf(" %.3lf", get_float(32, ptr + 28, jnt1 -> ENDIAN));
    printf(" %.3lf\n", get_float(32, ptr + 32, jnt1 -> ENDIAN));
    
    // Bounding sphere radius
    printf("Mesh bounding sphere radius (resp. to joint system): ");
    printf("%.3lf\n", get_float(32, ptr + 36, jnt1 -> ENDIAN));
    
    // Bounding box min
    printf("Mesh bounding box min XYZ (resp. to joint system):");
    printf(" %.3lf", get_float(32, ptr + 40, jnt1 -> ENDIAN));
    printf(" %.3lf", get_float(32, ptr + 44, jnt1 -> ENDIAN));
    printf(" %.3lf\n", get_float(32, ptr + 48, jnt1 -> ENDIAN));
    
    // Bounding box max
    printf("Mesh bounding box max XYZ (resp. to joint system):");
    printf(" %.3lf", get_float(32, ptr + 52, jnt1 -> ENDIAN));
    printf(" %.3lf", get_float(32, ptr + 56, jnt1 -> ENDIAN));
    printf(" %.3lf\n", get_float(32, ptr + 60, jnt1 -> ENDIAN));    
    printf("---\n");
    ptr += 64;
  }
  
  // joint position table
  printf("\n");
  ptr = jnt1 -> JNT_POS_TB; // temp pointer
  for (umax i = 0; i < jnt_count; i++, ptr += 2)
    printf("Joint %llu position in name table: %llu\n", i, get_std_uint(16, ptr, jnt1 -> ENDIAN));
  
  printf("Padding 1: "); // 4 multiple
  print_ascii_arr(jnt1 -> PAD_1, get_j3dgraph_padding_size((jnt1 -> PAD_1) - (jnt1 -> START), false));
  
  // joint string table
  printf("\n\n## Joint string table begin:\n");
  print_j3dgraph_str_tb(jnt1 -> JNT_STR_TB);  
  
  // final padding (32 bytes)
  printf("\nPadding 2: ");
  print_ascii_arr(jnt1 -> PAD_2, get_j3dgraph_padding_size((jnt1 -> PAD_2) - (jnt1 -> START), true));
  
  // end of structure
  printf("\n### J3DGraph JNT1 section info end ###\n");
  return;
}
